﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "exception.h"
#include "CommonPacket.h"
#include "ServerLoader.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CServerLoader

IMPLEMENT_CLASS_LOADER( CServerLoader, NET )
IMPLEMENT_CLASS_LOADER( CServerLoader, Frame )

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
Int CServerLoader::ThreadExceptHandler(TId tThreadId, Int nThreadType, Int nThreadRoutine, void*, struct _EXCEPTION_POINTERS* pExceptInfo)
{
    CString strName = TF("ThreadExceptHandler");
    strName.AppendFormat(TF("[%d-%d-%d]"), tThreadId, nThreadType, nThreadRoutine);
    CSEHException::Dump(pExceptInfo, *strName);
    return EXCEPTION_CONTINUE_SEARCH;
}
#endif

CServerLoader::CServerLoader(void)
: m_FileLog(false)
{
}

CServerLoader::~CServerLoader(void)
{
}

bool CServerLoader::Init(PCXStr pszCmd)
{
    if (pszCmd != nullptr)
    {
        m_Config.SetArgs(pszCmd);
    }
    if (m_Config.Init() && InitSystem())
    {
        return m_MainHandler.Init();
    }
    return false;
}

bool CServerLoader::Init(Int nArgc, PXStr* ppszArgv)
{
    if (nArgc > 1)
    {
        m_Config.SetArgs(nArgc, ppszArgv);
    }
    if (m_Config.Init() && InitSystem())
    {
        return m_MainHandler.Init();
    }
    return false;
}

void CServerLoader::Exit(void)
{
    m_MainHandler.Exit();
    ExitSystem();
    m_Config.Exit();
}

void CServerLoader::Start(void)
{
    if (m_MainHandler.Start())
    {
        m_SyncEvent.Wait();
    }
    m_MainHandler.Stop();
}

void CServerLoader::Stop(void)
{
    m_SyncEvent.Signal();
}

bool CServerLoader::InitSystem(void)
{
    if (m_FileLog.IsValid() == false)
    {
        m_FileLog.SetLevel(m_Config.GetMgrLogLevel());

        CString strPrefix = TF("ServerLoader");
        PCXStr pszExt  = CXChar::RevChr(m_Config.m_strConfigName.GetBuffer(), TF('.'));
        PCXStr pszName = CXChar::RevChr(m_Config.m_strConfigName.GetBuffer(), CServerConfig::PathSepChar);
        if (pszName == nullptr)
        {
            pszName = m_Config.m_strConfigName.GetBuffer();
        }
        if (pszExt > pszName)
        {
            strPrefix.FillBuffer(pszName, (pszExt - pszName));
        }
        else
        {
            strPrefix = pszName;
        }
        m_FileLog.Create(nullptr, *strPrefix);
    }
    DumpPlatform();

    if (INIT_CLASS_LOADER( CServerLoader, NET, UUID_OF( CNetworkSystem ), NETWORK_MODULE_NAME) == false)
    {
        LOG_ERROR(m_FileLog, TF("加载网络模块失败!"));
        return false;
    }
    LOADER_CLASS_CREEATE(CServerLoader, NET, UUID_OF(CNetwork), m_NetworkPtr.Cast<CComponent>());
    if (m_NetworkPtr == nullptr)
    {
        LOG_ERROR(m_FileLog, TF("创建网络实例失败!"));
        return false;
    }
    CNETTraits::NET_ATTR attr;
    attr.nAttrs     = m_Config.GetNetworkAttr();
    attr.nThread    = m_Config.GetNetworkThread();
    attr.nAckTime   = m_Config.GetNetworkAck();
    attr.nTimeout   = m_Config.GetNetworkTimeout();
    attr.nMaxBuffer = m_Config.GetNetworkBuffer();
    attr.nMaxJumbo  = m_Config.GetNetworkJumbo();
    if (attr.nAttrs & CNETTraits::ATTR_NAGLE)
    {
        attr.nAttrs &= (~CNETTraits::ATTR_NAGLE); // 去掉ATTR_NAGLE, 保持心态包的即使收发
    }
    if (m_NetworkPtr->Init(attr, nullptr, MNEW CPAKPackBuild) != RET_OKAY)
    {
        LOG_ERROR(m_FileLog, TF("初始化网络模块失败!"));
        return false;
    }
    m_Config.GetKeyValue().SetValue(CServerConfig::NetworkPtr, (void*)m_NetworkPtr.Get());

    if (INIT_CLASS_LOADER( CServerLoader, Frame, UUID_OF( CServerSystem ), m_Config.GetFramePath()) == false)
    {
        LOG_ERROR(m_FileLog, TF("加载服务模块失败!"));
        return false;
    }
    LOG_INFO(m_FileLog, TF("加载模块完成!"));
    return true;
}

void CServerLoader::ExitSystem(void)
{
    if (m_NetworkPtr != nullptr)
    {
        m_NetworkPtr->Exit();
        m_NetworkPtr = nullptr;
        m_Config.GetKeyValue().Remove(CServerConfig::NetworkPtr);
    }
    LOG_INFO(m_FileLog, TF("释放模块完成!"));

    EXIT_CLASS_LOADER( CServerLoader, NET );
    EXIT_CLASS_LOADER( CServerLoader, Frame );
}

void CServerLoader::DumpPlatform(void)
{
    UInt uRuntime = CPlatform::GetRuntimeConfig();
    LOG_INFO(m_FileLog, TF("---Platform Information---"));
    LOGV_INFO(m_FileLog, TF(" %s"), TF(__ARCH_TARGET_STR__));
    LOGV_INFO(m_FileLog, TF(" %s"), TF(__PLATFORM_TARGET_STR__));
    LOGV_INFO(m_FileLog, TF(" %s"), TF(__RUNTIME_CHARSET_STR__));
    LOGV_INFO(m_FileLog, TF(" %s"), (uRuntime & RUNTIME_CONFIG_DEBUG) ? TF("Debug Status") : TF("Release Status"));
    LOGV_INFO(m_FileLog, TF(" %s"), (uRuntime & RUNTIME_CONFIG_STATIC) ? TF("Static Library") : TF("Dynamic Library"));

    CPlatform::CPUINFO cpui;
    CPlatform::GetCPUInfo(cpui);
    LOG_INFO(m_FileLog, TF("---CPU Information---"));
    LOGV_INFO(m_FileLog, TF(" %s"), cpui.szInfo);
    LOGV_INFO(m_FileLog, TF(" Cores : %d"), cpui.uCores);

    CPlatform::MEMINFO mem;
    CPlatform::GetMemInfo(mem);
    LOG_INFO(m_FileLog, TF("---Memory Information---"));
    LOGV_INFO(m_FileLog, TF(" Memory Current Used       : %8d[%%]"), mem.uUsedPercent);
    LOGV_INFO(m_FileLog, TF(" Memory Page Size          : %8d[KB]"), mem.uPageSize);
    LOGV_INFO(m_FileLog, TF(" Memory Total Physical Size: %8d[MB]"), mem.uTotalPhys);
    LOGV_INFO(m_FileLog, TF(" Memory Avail Physical Size: %8d[MB]"), mem.uAvailPhys);

    CPlatform::OSINFO os;
    CPlatform::GetOSInfo(os);
    LOG_INFO(m_FileLog, TF("---Operation System Information---"));
    LOGV_INFO(m_FileLog, TF(" OS Type        : %s"), GetOSType(os.uOSType));
    LOGV_INFO(m_FileLog, TF(" OS Version     : %d.%d Build %d"), os.uMajor, os.uMinor, os.uBuild);
    LOGV_INFO(m_FileLog, TF(" OS Description : %s"), os.szInfo);

    CPlatform::TIMEINFO time;
    CPlatform::GetTimeInfo(time);
    LOGV_INFO(m_FileLog, TF("Time : %d-%d-%d(%d), %d:%d:%d:%d"), time.usYear, time.usMonth, time.usDay, time.usWeekDay, time.usHour, time.usMinute, time.usSecond, time.usMSecond);
    m_FileLog.Flush();
}

PCXStr CServerLoader::GetOSType(UInt uOSType)
{
    switch (uOSType)
    {
        CASE_RETSTR(CPlatform::OST_UNKNOWN, TF("Unknown OS"));
        CASE_RETSTR(CPlatform::OST_WINDOWS, TF("Windows"));
        CASE_RETSTR(CPlatform::OST_LINUX, TF("Linux"));
        CASE_RETSTR(CPlatform::OST_MAC, TF("Mac"));
        CASE_RETSTR(CPlatform::OST_IOS, TF("iOS"));
        CASE_RETSTR(CPlatform::OST_ANDROID, TF("Android"));
    default:
        {
            return TF("Unknown OS Type");
        }
    }
}

